﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TradingAppDal;
using System.Threading;
using System.Data.SqlTypes;

namespace TradingApp.Helper
{
    public class AnalysisHelper
    {
        private static Random oRandom = new Random();
        public static void AnalyzeSecurity(SecurityDal oSecurityDal)
        {

            int milSec = oRandom.Next(1, 5);
            Thread.Sleep(milSec);
            List<CorpActionsDal> lstCorpActions =  oSecurityDal.GetSecurityCorpActions(oSecurityDal.SecOrder_ID.Value);
            List<DividentsDal> lstDividents = oSecurityDal.GetSecurityDividents(oSecurityDal.SecOrder_ID.Value);
            List<IdentifiedStraddlesDal> lstIdentifiedStraddles = oSecurityDal.GetSecurityIdentifiedStraddles(oSecurityDal.SecOrder_ID.Value);
            List<M1Dal> lstM1a = oSecurityDal.GetSecurityM1Dal(oSecurityDal.SecOrder_ID.Value);
            List<SecMasterDal> lstSecMasters = oSecurityDal.GetSecuritySecMasters(oSecurityDal.SecOrder_ID.Value);

            List<InProcessSecurityDal> lstInProcessSecurity = oSecurityDal.GetSecurityInProcess(oSecurityDal.SecOrder_ID.Value);
            List<TransactionsDal> lst = oSecurityDal.GetSecurityTransactions(oSecurityDal.SecOrder_ID.Value);
            SqlDateTime Tran_Client_Open_ID = SqlDateTime.Null;
            for (int i = 0; i < lst.Count; i++)
            {
                if (i == 3)
                {
                    //Tran_Client_Open_ID = lst[i].Tran_Client_Open_ID;
                }
            }
        }
        public static void AnalyzeSecurityByPolark(long SecurityOrderId)
        {

            SecurityDal oSecurityDal = new SecurityDal();
            // getting sorted data as mentioned from the views itself
            List<M1Dal> lstM1s = oSecurityDal.GetSecurityM1Dal(SecurityOrderId);
            List<TransactionsDal> lstTransactions = oSecurityDal.GetSecurityTransactions(SecurityOrderId);

            
            // get Security Master for all  transactions
            List<SecMasterDal> lstSecMaster = oSecurityDal.GetSecuritySecMasters(SecurityOrderId);
            foreach (TransactionsDal oTrans in lstTransactions)
            {
                SecMasterDal oSecMasterDal = lstSecMaster.Find(p => p.Sec_Client_ID == oTrans.Sec_Client_ID);
                oTrans.SecMasterData = oSecMasterDal;
                if(oSecMasterDal != null)
                {
                    oTrans.NewTran_Quantity = oTrans.Tran_Quantity * GetNullReplacement(oSecMasterDal.Sec_Client_Und_SharesContract) * GetNullReplacement(oSecMasterDal.Sec_Client_Sim_SharesContract) * GetNullReplacement(oSecMasterDal.Sec_Client_Ident_SharesContract);
                }
                
            }
            for (int outer = 0; outer < lstTransactions.Count; outer++)
            {
                for (int inner = 0; inner < lstM1s.Count; inner++)
                {
                    // write ur logic 
                }
            }
        }
        private static Decimal GetNullReplacement(Decimal? DecValue)
        {
            if (!DecValue.HasValue)
            {
                return 1;
            }
            else
            {
                return DecValue.Value;
            }
        }
        public static void AnalyzeSecurity(long SecurityOrderId)
        {

            SecurityDal oSecurityDal = new SecurityDal();
            // getting sorted data as mentioned from the db itself
            List<M1Dal> lstM1s = oSecurityDal.GetSecurityM1Dal(SecurityOrderId);
            List<TransactionsDal> lstTransactions = oSecurityDal.GetSecurityTransactions(SecurityOrderId);

            //list to have merged records
            List<TransactionsDal> MergedTransactionList = new List<TransactionsDal>();
            //Step 1
            ListConsolidation(lstM1s, lstTransactions, MergedTransactionList);

            // get Security Master for all merged transactions
            List<SecMasterDal> lstSecMaster = oSecurityDal.GetSecuritySecMasters(SecurityOrderId);
            foreach (TransactionsDal oTrans in MergedTransactionList)
            {
                SecMasterDal oSecMasterDal = lstSecMaster.Find(p => p.Sec_Client_ID == oTrans.Sec_Client_ID);
                oTrans.SecMasterData = oSecMasterDal;
                // this updation is not clear 
                    //Multiply NewList. Tran_Quantity * isNull(SecMaster. Sec_Client_Und_SharesContract,1) * isNull(SecMaster. Sec_Client_Sim_SharesContract,1) * isNull(SecMaster. Sec_Client_Ident_SharesContract,1).
                //as u said it needs to be stored in oTrans.Tran_Quantity
            }


            // Step 2
            ProcessMergedList(MergedTransactionList);
        }

        private static void ProcessMergedList(List<TransactionsDal> MergedTransactionList)
        {
            // logic here to get the dates in required order
            List<TransactionsDal> lstOpenDateSorted = MergedTransactionList.OrderBy(p => p.Tran_Open_Trade_Date).ToList();
            List<TransactionsDal> lstCloseDateSorted = MergedTransactionList.OrderBy(p => p.Tran_Close_Trade_Date).ToList();
            Stack<TransactionsDal> stkOpenDate = new Stack<TransactionsDal>();
            Stack<TransactionsDal> stkCloseDate = new Stack<TransactionsDal>();
            // we will push all sorted transactions in stack and will compare the top most items in both stacks
            for (int i = lstOpenDateSorted.Count; i >0 ; i--)
            {
                stkOpenDate.Push(lstOpenDateSorted[i - 1]);
            }
            for (int i = lstCloseDateSorted.Count; i > 0; i--)
            {
                stkCloseDate.Push(lstCloseDateSorted[i - 1]);
            }
            TransactionsDal MinOpenDateTrans = null;
            TransactionsDal MinCloseDateTrans = null;
            do
            {
                if (MinOpenDateTrans == null)
                {
                    if (stkOpenDate.Count > 0)
                    {
                        MinOpenDateTrans = stkOpenDate.Pop();
                    }
                    else
                    {
                        MinOpenDateTrans = stkCloseDate.Pop();
                    }
                }
                if (MinCloseDateTrans == null)
                {
                    MinCloseDateTrans = stkCloseDate.Pop();
                }
                if (MinOpenDateTrans.Tran_Open_Trade_Date <= MinCloseDateTrans.Tran_Close_Trade_Date)
                {
                    // do processing


                    // in end 
                    MinOpenDateTrans = null;
                }
                else
                {
                    // Check for close event

                    if (MinCloseDateTrans.IsM1Found &&  MinCloseDateTrans.Tran_GainLoss_Exchange + MinCloseDateTrans.Tran_GainLoss_Capital + MinCloseDateTrans.M1Data.WashSales_Exchange_Adjustment + MinCloseDateTrans.M1Data.WashSales_Capital_Adjustment < 0)
                    {
                        WashScaleCalculation(MinCloseDateTrans,MergedTransactionList);
                    }

                    // in end 
                    MinCloseDateTrans = null;
                }
            }
            while (stkOpenDate.Count > 0 && stkCloseDate.Count > 0);

        }

        private static void WashScaleCalculation(TransactionsDal MinCloseDateTrans, List<TransactionsDal> MergedTransactionList)
        {
            DateTime MinDate = !MinCloseDateTrans.Tran_Close_Trade_Date.HasValue ? DateTime.MinValue : MinCloseDateTrans.Tran_Close_Trade_Date.Value.AddDays(-30);
            DateTime MaxDate = MinCloseDateTrans.Tran_Close_Trade_Date.HasValue ? DateTime.MinValue : MinCloseDateTrans.Tran_Close_Trade_Date.Value.AddDays(30);
            TransactionsDal TargetItem = null;
            if(MinCloseDateTrans.Tran_Open_Trade_Date.HasValue  && MinDate != DateTime.MinValue && MaxDate != DateTime.MinValue)
            {
                TargetItem = MergedTransactionList.Find(p=> p.Tran_Open_Trade_Date.Value <= MaxDate && p.Tran_Open_Trade_Date.Value >= MinDate 
                    && p.Tran_Client_LongShort.ToUpper() == MinCloseDateTrans.Tran_Client_LongShort.ToUpper()
                    && (p.Sec_Client_ID == MinCloseDateTrans.Sec_Client_ID || (p.SecMasterData.Sec_Client_Und_ID == MinCloseDateTrans.Sec_Client_ID ))
                    && String.IsNullOrEmpty(MinCloseDateTrans.M1Data.WashSales_Unique_From_ID));
                if (TargetItem != null)
                {
                    MinCloseDateTrans.M1Data.WashSales_AssignedTo_Open_ID = TargetItem.Tran_Client_Open_ID;
                    MinCloseDateTrans.M1Data.WashSales_Unique_To_ID = Guid.NewGuid().ToString();
                   
                    TargetItem.M1Data.WashSales_AssignedFrom_Open_ID = MinCloseDateTrans.Tran_Client_Open_ID ;
                    TargetItem.M1Data.WashSales_AssignedFrom_Close_ID = MinCloseDateTrans.Tran_Client_Close_ID ;
                    TargetItem.M1Data.WashSales_LossDate = MinCloseDateTrans.Tran_Close_Trade_Date ;
                    TargetItem.M1Data.WashSales_Unique_From_ID = MinCloseDateTrans.M1Data.WashSales_Unique_To_ID ;
                    TargetItem.M1Data.WashSales_Capital_Adjustment = MinCloseDateTrans.M1Data.WashSales_Capital_Adjustment + MinCloseDateTrans.Tran_GainLoss_Capital;
                    TargetItem.M1Data.WashSales_Exchange_Adjustment = MinCloseDateTrans.M1Data.WashSales_Exchange_Adjustment + MinCloseDateTrans.Tran_GainLoss_Exchange;
                }
            }
            //what and how we need to save . Please answer save related questions from DB
            SaveData();
        }

        private static void SaveData()
        {
            //throw new NotImplementedException();
        }

        private static void ListConsolidation(List<M1Dal> lstM1s, List<TransactionsDal> lstTransactions, List<TransactionsDal> MergedTransactionList)
        {
            Stack<TransactionsDal> stk = new Stack<TransactionsDal>();
            foreach (TransactionsDal tran in lstTransactions)
            {
                stk.Push(tran);
                do
                {
                    TransactionsDal t = stk.Pop();
                    List<M1Dal> lstAssM1s = lstM1s.FindAll(p => p.Portfolio_Client_ID == t.Portfolio_Client_ID && p.Sec_Client_ID == t.Sec_Client_ID && p.Tran_Client_LongShort.ToUpper() == t.Tran_Client_LongShort.ToUpper() && p.Tran_Client_Open_ID == t.Tran_Client_Open_ID);
                    if (lstAssM1s != null && lstAssM1s.Count > 0)
                    {
                        M1Dal AssM1 = lstAssM1s[0];

                        if (t.Tran_Quantity > AssM1.Tran_Quantity)
                        {
                            // please verify
                            TransactionsDal oMerged = GetTransactionClone(t);
                            Decimal? InitialTran_Quantity = t.Tran_Quantity;
                            oMerged.Tran_Quantity = AssM1.Tran_Quantity;
                            t.Tran_Quantity = t.Tran_Quantity - AssM1.Tran_Quantity;
                            ProrateWhenTQuantityGreater(InitialTran_Quantity, oMerged, t);
                            oMerged.IsM1Found = true;
                            oMerged.M1Data = AssM1;
                            lstM1s.Remove(AssM1);
                            stk.Push(t);

                        }
                        if (t.Tran_Quantity < AssM1.Tran_Quantity)
                        {
                            // this case is not clear as we need to prorate M1Data items in this case - Please suggest
                            TransactionsDal oMerged = GetTransactionClone(t);
                            Decimal? InitialTran_Quantity = t.Tran_Quantity;
                            oMerged.Tran_Quantity = t.Tran_Quantity;
                            t.Tran_Quantity = AssM1.Tran_Quantity - t.Tran_Quantity;
                            ProrateWhenTQuantityLesser(InitialTran_Quantity, oMerged, t);
                            oMerged.IsM1Found = true;
                            oMerged.M1Data = AssM1;
                            lstM1s.Remove(AssM1);
                            stk.Push(t);
                        }
                        if (t.Tran_Quantity == AssM1.Tran_Quantity)
                        {
                            t.IsM1Found = true;
                            t.M1Data = AssM1;
                            lstM1s.Remove(AssM1);
                            MergedTransactionList.Add(t);
                        }


                    }
                    else
                    {
                        //if no associated transaction found just add it in the MergedTransationList
                        t.IsM1Found = false;
                        t.M1Data = new M1Dal();
                        MergedTransactionList.Add(t);
                    }
                }
                while (stk.Count > 0);
            }
        }

        private static void ProrateWhenTQuantityLesser(Decimal? InitialTran_Quantity, TransactionsDal oMerged, TransactionsDal t)
        {
            // ?
        }

        private static void ProrateWhenTQuantityGreater(Decimal? InitialTran_Quantity, TransactionsDal oMerged, TransactionsDal oOpenTransaction)
        {

            oMerged.Tran_Orig_Cost_Base = oMerged.Tran_Orig_Cost_Base * oMerged.Tran_Quantity / InitialTran_Quantity;
            oMerged.Tran_Amort_Cost_Base = oMerged.Tran_Amort_Cost_Base * oMerged.Tran_Quantity / InitialTran_Quantity;
            oMerged.Tran_ProceedsMarket_Principle_Base = oMerged.Tran_ProceedsMarket_Principle_Base * oMerged.Tran_Quantity / InitialTran_Quantity;
            oMerged.Tran_GainLoss_Exchange = oMerged.Tran_GainLoss_Exchange * oMerged.Tran_Quantity / InitialTran_Quantity;
            oMerged.Tran_GainLoss_Capital = oMerged.Tran_GainLoss_Capital * oMerged.Tran_Quantity / InitialTran_Quantity;

            oOpenTransaction.Tran_Orig_Cost_Base = oOpenTransaction.Tran_Orig_Cost_Base * oOpenTransaction.Tran_Quantity / InitialTran_Quantity;
            oOpenTransaction.Tran_Amort_Cost_Base = oOpenTransaction.Tran_Amort_Cost_Base * oOpenTransaction.Tran_Quantity / InitialTran_Quantity;
            oOpenTransaction.Tran_ProceedsMarket_Principle_Base = oOpenTransaction.Tran_ProceedsMarket_Principle_Base * oOpenTransaction.Tran_Quantity / InitialTran_Quantity;
            oOpenTransaction.Tran_GainLoss_Exchange = oOpenTransaction.Tran_GainLoss_Exchange * oOpenTransaction.Tran_Quantity / InitialTran_Quantity;
            oOpenTransaction.Tran_GainLoss_Capital = oOpenTransaction.Tran_GainLoss_Capital * oOpenTransaction.Tran_Quantity / InitialTran_Quantity;
        }
        public static TransactionsDal GetTransactionClone(TransactionsDal oTransactionsDal)
        {
            TransactionsDal Clone = new TransactionsDal();
            Clone.SecOrder_ID = oTransactionsDal.SecOrder_ID;
            Clone.Process_ID = oTransactionsDal.Process_ID;
            Clone.Portfolio_Client_ID = oTransactionsDal.Portfolio_Client_ID;
            Clone.Sec_Client_ID = oTransactionsDal.Sec_Client_ID;
            Clone.Tran_Option_ASP = oTransactionsDal.Tran_Option_ASP;
            Clone.Tran_Client_Transaction_Code = oTransactionsDal.Tran_Client_Transaction_Code;
            Clone.Tran_Client_Open_ID = oTransactionsDal.Tran_Client_Open_ID;
            Clone.Tran_Open_Trade_Date = oTransactionsDal.Tran_Open_Trade_Date;
            //Clone.Tran_Open_Settle_Date = oTransactionsDal.Tran_Open_Settle_Date;
            Clone.Tran_Client_Close_ID = oTransactionsDal.Tran_Client_Close_ID;
            Clone.Tran_Close_Trade_Date = oTransactionsDal.Tran_Close_Trade_Date;
            //Clone.Tran_Close_Settle_Date = oTransactionsDal.Tran_Close_Settle_Date;
            Clone.Tran_Client_LongShort = oTransactionsDal.Tran_Client_LongShort;
            Clone.Tran_Quantity = oTransactionsDal.Tran_Quantity;
            //Clone.Tran_Notional_Quantity = oTransactionsDal.Tran_Notional_Quantity;
            //Clone.Tran_ExpensesNotInBasis_Local = oTransactionsDal.Tran_ExpensesNotInBasis_Local;
            //Clone.Tran_ExpensesNotInBasis_Base = oTransactionsDal.Tran_ExpensesNotInBasis_Base;
            //Clone.Tran_Notional_Cost_Local = oTransactionsDal.Tran_Notional_Cost_Local;
            Clone.Tran_Orig_Cost_Base = oTransactionsDal.Tran_Orig_Cost_Base;
            Clone.Tran_Amort_Cost_Base = oTransactionsDal.Tran_Amort_Cost_Base;
            Clone.Tran_ProceedsMarket_Principle_Base = oTransactionsDal.Tran_ProceedsMarket_Principle_Base;
            Clone.Tran_GainLoss_Exchange = oTransactionsDal.Tran_GainLoss_Exchange;
            Clone.Tran_GainLoss_Capital = oTransactionsDal.Tran_GainLoss_Capital;
            Clone.Tran_GainLoss_Character = oTransactionsDal.Tran_GainLoss_Character;
            Clone.Tran_FileType = oTransactionsDal.Tran_FileType;

            return Clone;
        }
    }
    public class SecMasterInfo
    {
        public int Property1 { get; set; }
        public int Property2 { get; set; }
        public List<TransactionsDal> lstTransactions { get; set; }
    }
}
